﻿using System;
using System.Drawing;
using System.IO;

namespace NDK.Module.Model
{
    public  class CircleInterLine
    {
       public struct CirFunc
        {
            public double x, y, r;
        }
        struct LineFunc
        {
            public double k, b;
            public bool b90;
        }
        public enum TargetPt
        {
            CirCenterPtPosXPosY,//圆心所在的x的正方向，也就是取X>圆心X的交点
            CirCenterPtNegXNegY//圆心所在的x的负方向，也就是取X<圆心X的交点
        }

        /// <summary>
        /// 根据圆方程和直线方程，求取直线和圆的交点
        /// </summary>
        /// <param name="cir">圆方程参数</param>
        /// <param name="line">直线方程参数</param>
        /// <param name="tar">直线和圆一般有两个交点，这里指定需要的是哪个点</param>
        /// <returns></returns>
        private static PointD CalcInterPtWithLineAndCircle(CirFunc cir, LineFunc line, TargetPt tar = TargetPt.CirCenterPtPosXPosY)
        {
            PointD pt = new PointD();
            if (line.b90)
            {
                if (tar == TargetPt.CirCenterPtPosXPosY)
                    pt.y = (cir.y + cir.r);
                else pt.y = (cir.y - cir.r);
                pt.x = line.b;
                return pt;
            }


            double a = line.k * line.k + 1;
            double b = 2 * (line.b * line.k - cir.y * line.k - cir.x);
            double c = cir.x * cir.x + Math.Pow(line.b - cir.y, 2) - cir.r * cir.r;

            double x1 = (-b + Math.Sqrt(b * b - 4 * a * c)) / (2 * a);
            double y1 = line.k * x1 + line.b;
            double x2 = (-b - Math.Sqrt(b * b - 4 * a * c)) / (2 * a);
            double y2 = -(line.k * x1 + line.b);

            if (tar == TargetPt.CirCenterPtPosXPosY)
            {
                if (x1 > cir.x || y1>cir.y) { pt.x = (float)x1; pt.y = (float)y1; }
                else if(x1>cir.x && y1 > cir.y) { pt.x = (float)x1; pt.y = (float)y1; }
                else if (x1 == cir.x)
                {
                    if (y1 > cir.y) { pt.x = (float)x1; pt.y = (float)y1; }
                    else { pt.x = (float)x2; pt.y = (float)y2; }
                }
                else { pt.x = (float)x2; pt.y = (float)y2; }
            }
            else
            {
                if (x1 < cir.x || y1 < cir.y) { pt.x= (float)x1; pt.y = (float)y1; }
                else if (x1 < cir.x && y1 < cir.y) { pt.x = (float)x1; pt.y = (float)y1; }
                else if (x1 == cir.x)
                {
                    if (y1 < cir.y) { pt.x = (float)x1; pt.y = (float)y1; }
                    else { pt.x = (float)x2; pt.y = (float)y2; }
                }
                else { pt.x = (float)x2; pt.y = (float)y2; }
            }

            return pt;
        }

        /// <summary>
        /// 根据一个经过点，和直线角度得出直线方程的相关参数
        /// </summary>
        /// <param name="degree">角度(非弧度)</param>
        /// <param name="pt">经过点</param>
        /// <returns></returns>
        private static LineFunc GetLineByDegree(double degree, PointF pt)
        {
            if (degree >= 89.99 && degree<=90.01) return new LineFunc() { b90 = true, b = pt.X };
            double rad = degree * Math.PI / 180;
            //if(rad==Math.PI/2)return LineFunc(){ }
            double k = Math.Tan(rad);
            double b = pt.Y - k * pt.X;
            return new LineFunc() { k = k, b = b, b90 = false };
        }


        public struct Dist
        {
            public double X, Y, A;
        }
        public static Dist GetDist(double degree,bool blongtoshort)
        {
            var width = Common.pPrjPara.PhoneWidth;
            var height = Common.pPrjPara.PhoneHeight;
            var radius = Common.pPrjPara.PhoneRadius;

            if (blongtoshort) { width = Common.pPrjPara.PhoneWidth; height = Common.pPrjPara.PhoneHeight; }
            else { width = Common.pPrjPara.PhoneHeight; height = Common.pPrjPara.PhoneWidth; }

            var initDistX = width / 2;
            var initDistY = height / 2 - radius;

            CirFunc circle = new CirFunc();
            circle.x = width / 2 - radius;
            circle.y = height / 2 - radius;
            circle.r = radius;

            var line = GetLineByDegree(degree, new PointF((float)circle.x, (float)circle.y));
            var pt = CalcInterPtWithLineAndCircle(circle, line);
            var nowDist = GetDist(pt, new PointD { x = 0, y = 0 });

            var initAngle = Math.Atan(pt.y / pt.y);

            var nowDistX = nowDist * Math.Cos(initAngle - degree / 180.0 * Math.PI);
            var nowDistY = nowDist * Math.Sin(initAngle - degree / 180.0 * Math.PI);

            return new Dist() { X = -nowDistX + initDistX, Y = -nowDistY + initDistY };

            //var line = GetLineByDegree(degree, new PointF((float)circle.x, (float)circle.y));
            //var pt = CalcInterPtWithLineAndCircle(circle, line);

            //var nowDist = GetDist(pt, new PointF(0, 0));
            //var nowDistX = nowDist * Math.Cos(initAngle);
            //var nowDistY = nowDist * Math.Sin(initAngle);

            //var initCenterAngle = Math.Atan((height / 2 - radius) / (width / 2 - radius));
            //var initCenterDistX = width / 2 - radius;
            //var initCenterDistY = height / 2 - radius;
            //var centerDist = Math.Sqrt(Math.Pow(initCenterDistX, 2) + Math.Pow(initCenterDistY, 2));

            //var deltaX = centerDist * Math.Cos(initCenterAngle - degree / 180.0 * Math.PI) - initCenterDistX;
            //var deltaY = centerDist * Math.Sin(initCenterAngle - degree / 180.0 * Math.PI) - initCenterDistY;

            //return new Dist() { X = nowDistX - initDistX + deltaX, Y = nowDistY - initDistY + deltaY };
        }
        private static double GetDist(PointD pt1, PointD pt2)
        {
            return Math.Abs( Math.Sqrt(Math.Pow(pt2.x - pt1.x, 2) + Math.Pow(pt2.y - pt1.y, 2)));
        }




        public static Dist GetDist2(double degree, bool blongtoshort)
        {//非1/4圆，拟合曲线
            var width = Common.pPrjPara.PhoneWidth;
            var height = Common.pPrjPara.PhoneHeight;
            var radius = Common.pPrjPara.PhoneRadius;

            if (blongtoshort) { width = Common.pPrjPara.PhoneWidth; height = Common.pPrjPara.PhoneHeight; }
            else { width = Common.pPrjPara.PhoneHeight; height = Common.pPrjPara.PhoneWidth; }

            var initDistX = width / 2;
            var initDistY = height / 2 - radius;

            CirFunc circle = new CirFunc();
            circle.x = width / 2 - radius;
            circle.y = height / 2 - radius;
            circle.r = radius;

            var line = GetLineByDegree(degree, new PointF((float)circle.x, (float)circle.y));
            var pt = CalcInterPtWithLineAndCircle(circle, line);
            pt = CalcInterPtWithLineAndCircle((double)pt.x);
            var nowDist = GetDist(pt, new PointD { x = 0, y = 0 });

            var initAngle = Math.Atan(pt.y / pt.x);

            var nowDistX = nowDist * Math.Cos(initAngle - degree / 180.0 * Math.PI);
            var nowDistY = nowDist * Math.Sin(initAngle - degree / 180.0 * Math.PI);

            return new Dist() { X = -nowDistX + initDistX, Y = -nowDistY + initDistY };
        }

        private static PointD CalcInterPtWithLineAndCircle(double x)
        {
            var a = 73.553932;
            var b = -0.000177;
            var c = -0.304683;
            var d = -2.34 * Math.Pow(Math.E, -88);
            var f = -5.677898;

            double y = a;
            y += b * Math.Pow(Math.E, -1 * c * x);
            y += d * Math.Pow(Math.E, -1 * f * x);
            return new PointD() { x = x, y = y };
        }


        public struct PointD
        {
            public double x;
            public double y;
        }



        public static Dist GetDistTest(double degree, bool blongtoshort)
        {
            var width = Common.pPrjPara.PhoneWidth;
            var height = Common.pPrjPara.PhoneHeight;
            var radius = Common.pPrjPara.PhoneRadius;

            var initDistX = 35.76203;
            var initDistY = 58.71137;

            if (blongtoshort) { width = Common.pPrjPara.PhoneWidth; height = Common.pPrjPara.PhoneHeight; }
            else { width = Common.pPrjPara.PhoneHeight; height = Common.pPrjPara.PhoneWidth; initDistX = 73.35326; initDistY = 21.12017; }


            CirFunc circle = new CirFunc();
            circle.x = width / 2 - radius;
            circle.y = height / 2 - radius;
            circle.r = radius;

            var deg = 90 / (8.71 * 2 + 76.83);

            if(degree <= 8.71*deg)
            {
                circle.x = 2.85549;
                circle.y = 59.5833;
                circle.r = 32.91809;
                if (!blongtoshort) { circle.x = 40.44672; circle.y = 21.99202; }
            }
            else if(degree <= (8.71+76.83) * deg)
            {
                circle.x = 24.88794;
                circle.y = 62.47690;
                circle.r = 10.6993;
                if (!blongtoshort) { circle.x = 62.47690; circle.y = 24.88794; }
            }
            else
            {
                circle.x = 21.99202;
                circle.y = 40.44672;
                circle.r = 32.91809;
                if (!blongtoshort) { circle.x = 59.5833; circle.y = 2.85549; }
            }

            var line = GetLineByDegree(degree, new PointF((float)circle.x, (float)circle.y));
            var pt = CalcInterPtWithLineAndCircle(circle, line);
            var nowDist = GetDist(pt, new PointD { x = 0, y = 0 });

            var initAngle = Math.Atan(pt.y / pt.x);

            var nowDistX = nowDist * Math.Cos(initAngle - degree / 180.0 * Math.PI);
            var nowDistY = nowDist * Math.Sin(initAngle - degree / 180.0 * Math.PI);

            var xlen = -nowDistX + initDistX;
            var ylen = -nowDistY + initDistY;
            ylen = ylen < 0 ? 0 : ylen;
            ylen = ylen > 79.835 ? 79.835 : ylen;
            return new Dist() { X = xlen, Y =ylen};
        }

        public static Dist GetDistTest1(double degree, bool blongtoshort)
        {
            var width = Common.pPrjPara.PhoneWidth;
            var height = Common.pPrjPara.PhoneHeight;
            var radius = Common.pPrjPara.PhoneRadius;

            var initDistX = 35.76203;
            var initDistY = 58.71137;

            if (blongtoshort) { width = Common.pPrjPara.PhoneWidth; height = Common.pPrjPara.PhoneHeight; }
            else { width = Common.pPrjPara.PhoneHeight; height = Common.pPrjPara.PhoneWidth; initDistX = 73.35326; initDistY = 21.12017; }


            CirFunc circle = new CirFunc();
            circle.x = width / 2 - radius;
            circle.y = height / 2 - radius;
            circle.r = radius;

            var deg = 90 / (8.71 * 2 + 76.83);
            var rad = degree / 180 * Math.PI;

            if (degree<=9.96)
            {
                circle.x = 2.85549;
                circle.y = 59.5833;
                circle.r = 32.91809;
                if (!blongtoshort) { circle.x = 40.44672; circle.y = 21.99202; }
            }
            else if (degree <= (8.71+76.3))
            {
                circle.x = 24.88794;
                circle.y = 62.47690;
                circle.r = 10.6993;
                if (!blongtoshort) { circle.x = 62.47690; circle.y = 24.88794; }
            }
            else
            {
                circle.x = 21.99202;
                circle.y = 40.44672;
                circle.r = 32.91809;
                if (!blongtoshort) { circle.x = 59.5833; circle.y = 2.85549; }
            }

            //var initDistX = width / 2;
            //var initDistY = height / 2 - radius;

            CirFunc circle1 = new CirFunc();
            circle1.x = width / 2 - radius;
            circle1.y = height / 2 - radius;
            circle1.r = radius;
            //var c3 = new CirFunc();
            //c3.x = width / 2;
            //c3.y = height / 2 - radius;

            //var lenLine1 = Math.Sqrt(circle1.x * circle1.x + circle1.y * circle1.y);
            //var angle1 = Math.Atan(circle1.y / circle1.x);
            //var lenLine2 = Math.Sqrt(c3.x * c3.x + c3.y * c3.y);
            //var angle2 = Math.Atan(c3.y / c3.x);

            //var pt1 = new PointD() { x = lenLine1 * Math.Cos(angle1 - rad), y = lenLine1 * Math.Sin(angle1 - rad) };
            //var pt2 = new PointD() { x = lenLine2 * Math.Cos(angle2 - rad), y = lenLine2 * Math.Sin(angle2 - rad) };
            //var angleDetla =Math.Abs( Math.Atan((pt2.y - pt1.y) / (pt2.x - pt1.x)));

            var line = GetLineByDegree(degree, new PointF((float)circle.x, (float)circle.y));
            var pt = CalcInterPtWithLineAndCircle(circle, line);
            //if(degree<=2 || degree>=88)
            //{
            //     line = GetLineByDegree(degree, new PointF((float)circle1.x, (float)circle1.y));
            //     pt = CalcInterPtWithLineAndCircle(circle1, line);
            //}
            var nowDist = GetDist(pt, new PointD { x = 0, y = 0 });

            var initAngle = Math.Atan(pt.y / pt.x);

            var nowDistX =Math.Abs(  nowDist) * Math.Cos(initAngle - rad);
            var nowDistY =Math.Abs( nowDist) * Math.Sin(initAngle - rad);

            var xlen = -nowDistX + initDistX;
            var ylen = -nowDistY + initDistY;
            //xlen = xlen > 37.59 ? 37.59 : xlen;
            //ylen = ylen < 0 ? 0 : ylen;
            //ylen = ylen > 79.835 ? 79.835 : ylen;
            return new Dist() { X = xlen, Y =ylen , A= degree };
        }

        public static Dist GetDistTest2(double degree, bool blongtoshort)
        {
            var width = Common.pPrjPara.PhoneWidth;
            var height = Common.pPrjPara.PhoneHeight;
            var radius = Common.pPrjPara.PhoneRadius;

            var initDistX = 35.76203;
            var initDistY = 58.71137;

            if (blongtoshort) { width = Common.pPrjPara.PhoneWidth; height = Common.pPrjPara.PhoneHeight; }
            else { width = Common.pPrjPara.PhoneHeight; height = Common.pPrjPara.PhoneWidth; initDistX = 73.35326; initDistY = 21.12017; }

            CirFunc circle = new CirFunc();
            circle.x = width / 2 - radius;
            circle.y = height / 2 - radius;
            circle.r = radius;

            var deg = 90 / (8.71 * 2 + 76.83);
            var rad = degree / 180 * Math.PI;

            CirFunc circle1 = new CirFunc();
            circle1.x = width / 2 - radius;
            circle1.y = height / 2 - radius;
            circle1.r = radius;

            var data = "";
            using (StreamReader sr=new StreamReader("E:\\pts.csv"))
            {
                data=sr.ReadToEnd();
            }
           var dataLine= data.Split('\n');
            foreach (var pt in dataLine)
            {
                if (string.IsNullOrWhiteSpace(pt) || !pt.Contains(",")) continue;
                var x = double.Parse( pt.Split(',')[0]);
                var y =double.Parse( pt.Split(',')[1]);
                var a =double.Parse( pt.Split(',')[2]);

                var len = Math.Sqrt(x * x + y * y);
                var angle = Math.Atan(y / x);
                var newX = len * Math.Cos(angle - a);
                var newY = len * Math.Sin(angle - a);

                var dist = new Dist() { X = newX - x, Y = newY - y, A = a };
            }
            //var line = GetLineByDegree(degree, new PointF((float)circle.x, (float)circle.y));
            //var pt = CalcInterPtWithLineAndCircle(circle, line);

            //var nowDist = GetDist(pt, new PointD { x = 0, y = 0 });

            //var initAngle = Math.Atan(pt.y / pt.x);

            //var nowDistX = Math.Abs(nowDist) * Math.Cos(initAngle - rad);
            //var nowDistY = Math.Abs(nowDist) * Math.Sin(initAngle - rad);

            //var xlen = -nowDistX + initDistX;
            //var ylen = -nowDistY + initDistY;
            ////xlen = xlen > 37.59 ? 37.59 : xlen;
            ////ylen = ylen < 0 ? 0 : ylen;
            ////ylen = ylen > 79.835 ? 79.835 : ylen;
            return new Dist() { X = 0, Y = 0, A = degree };
        }

        public static Dist GetDist0423(double degree, bool blongtoshort)
        {
            CirFunc circle = new CirFunc();
            var deg = degree / 180.0 * Math.PI;


            PointD ptFirst;

            circle = GetCircleCenter(degree, blongtoshort,out ptFirst);

                var lenLine1 = Math.Sqrt(circle.x * circle.x+ circle.y * circle.y);
                var angle1 = Math.Atan(circle.y / circle.x);
                var lenLine2 = 68.74555;
                var angle2 = Math.Atan(58.71137 / 35.76206);

                var pt1 = new PointD() { x = lenLine1 * Math.Cos(angle1 - deg), y = lenLine1 * Math.Sin(angle1 - deg) };
                var pt2 = new PointD() { x = lenLine2 * Math.Cos(angle2 - deg), y = lenLine2 * Math.Sin(angle2 - deg) };
                var angleDetla =Math.Atan((pt2.y - pt1.y) / (pt2.x - pt1.x)) -Math.Atan((58.71137-circle.y)/(35.76206-circle.x));


                var line = GetLineByDegree(angleDetla/Math.PI*180, new PointF((float)circle.x, (float)circle.y));
                var pt = CalcInterPtWithLineAndCircle(circle, line);
                var nowDist = GetDist(pt, new PointD { x = 0, y = 0 });

                var initAngle = Math.Atan(pt.y / pt.x);

                var nowDistX = nowDist * Math.Cos(initAngle - deg);
                var nowDistY = nowDist * Math.Sin(initAngle - deg);

                var initDistX = 35.76206;
                var initDistY = 58.71137;
                return new Dist() { X = pt.x-  initDistX, Y = pt.y - initDistY };


        }

        public static CirFunc GetCircleCenter(double degree,bool blongtoshort,out PointD ptFirst)
        {
            CirFunc circle = new CirFunc();
            ptFirst = new PointD();
            var deg = 90 / (8.71 * 2 + 76.83);
            if (degree <= 8.71 * deg)
            {
                circle.x = 2.85549;
                circle.y = 59.5833;
                circle.r = 32.91809;
                if (!blongtoshort) { circle.x = 40.44672; circle.y = 21.99202; }
            }
            else if (degree <= (8.71 + 76.83) * deg)
            {
                circle.x = 24.88794;
                circle.y = 62.47690;
                circle.r = 10.6993;
                if (!blongtoshort) { circle.x = 62.47690; circle.y = 24.88794; }
            }
            else
            {
                circle.x = 21.99202;
                circle.y = 40.44672;
                circle.r = 32.91809;
                if (!blongtoshort) { circle.x = 59.5833; circle.y = 2.85549; }
            }
            return circle;
        }


        public static Dist GetDistTest12(double degree, bool blongtoshort)
        {//三段圆弧模式，圆弧起始终止点有拉伸和压缩
            var width = Common.pPrjPara.PhoneWidth;
            var height = Common.pPrjPara.PhoneHeight;
            var radius = Common.pPrjPara.PhoneRadius;

            var initDistX = 35.76203;
            var initDistY = 58.71137;

            if (blongtoshort) { width = Common.pPrjPara.PhoneWidth; height = Common.pPrjPara.PhoneHeight; }
            else { width = Common.pPrjPara.PhoneHeight; height = Common.pPrjPara.PhoneWidth; initDistX = 73.35326; initDistY = 21.12017; }


            CirFunc circle = new CirFunc();
            circle.x = width / 2 - radius;
            circle.y = height / 2 - radius;
            circle.r = radius;

            var deg = 90 / (8.71 * 2 + 76.83);
            var rad = degree / 180 * Math.PI;

            if (degree <= 8.71)
            {
                circle.x = 2.85549;
                circle.y = 59.5833;
                circle.r = 32.91809;
                if (!blongtoshort) { circle.x = 40.44672; circle.y = 21.99202; }
            }
            else if (degree <= (8.71 + 76.3))
            {
                circle.x = 24.88794;
                circle.y = 62.47690;
                circle.r = 10.6993;
                if (!blongtoshort) { circle.x = 62.47690; circle.y = 24.88794; }
            }
            else
            {
                circle.x = 21.99202;
                circle.y = 40.44672;
                circle.r = 32.91809;
                if (!blongtoshort) { circle.x = 59.5833; circle.y = 2.85549; }
            }

            //var initDistX = width / 2;
            //var initDistY = height / 2 - radius;

            CirFunc circle1 = new CirFunc();
            circle1.x = width / 2 - radius;
            circle1.y = height / 2 - radius;
            circle1.r = radius;
            //var c3 = new CirFunc();
            //c3.x = width / 2;
            //c3.y = height / 2 - radius;

            //var lenLine1 = Math.Sqrt(circle1.x * circle1.x + circle1.y * circle1.y);
            //var angle1 = Math.Atan(circle1.y / circle1.x);
            //var lenLine2 = Math.Sqrt(c3.x * c3.x + c3.y * c3.y);
            //var angle2 = Math.Atan(c3.y / c3.x);

            //var pt1 = new PointD() { x = lenLine1 * Math.Cos(angle1 - rad), y = lenLine1 * Math.Sin(angle1 - rad) };
            //var pt2 = new PointD() { x = lenLine2 * Math.Cos(angle2 - rad), y = lenLine2 * Math.Sin(angle2 - rad) };
            //var angleDetla =Math.Abs( Math.Atan((pt2.y - pt1.y) / (pt2.x - pt1.x)));

            var line = GetLineByDegree(degree, new PointF((float)circle.x, (float)circle.y));
            var pt = CalcInterPtWithLineAndCircle(circle, line);
            //if(degree<=2 || degree>=88)
            //{
            //     line = GetLineByDegree(degree, new PointF((float)circle1.x, (float)circle1.y));
            //     pt = CalcInterPtWithLineAndCircle(circle1, line);
            //}
            var nowDist = GetDist(pt, new PointD { x = 0, y = 0 });

            var initAngle = Math.Atan(pt.y / pt.x);

            var nowDistX = Math.Abs(nowDist) * Math.Cos(initAngle - rad);
            var nowDistY = Math.Abs(nowDist) * Math.Sin(initAngle - rad);

            var xlen = -nowDistX + initDistX;
            var ylen = -nowDistY + initDistY;
            //xlen = xlen > 37.59 ? 37.59 : xlen;
            //ylen = ylen < 0 ? 0 : ylen;
            //ylen = ylen > 79.835 ? 79.835 : ylen;
            return new Dist() { X = xlen, Y = ylen, A = degree };
        }
    }
}
